package com.xuecheng.content.service.impl;

import com.alibaba.fastjson.JSON;
import com.xuecheng.base.exception.CommonError;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.content.config.MultipartSupportConfig;
import com.xuecheng.content.feignclient.MediaServiceClient;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.mapper.CourseMarketMapper;
import com.xuecheng.content.mapper.CoursePublishMapper;
import com.xuecheng.content.mapper.CoursePublishPreMapper;
import com.xuecheng.content.model.dto.CourseBaseInfoDto;
import com.xuecheng.content.model.dto.CoursePreviewDto;
import com.xuecheng.content.model.dto.TeachplanDto;
import com.xuecheng.content.model.po.*;
import com.xuecheng.content.service.*;
import com.xuecheng.messagesdk.mapper.MqMessageMapper;
import com.xuecheng.messagesdk.model.po.MqMessage;
import com.xuecheng.messagesdk.service.MqMessageService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class CoursePublishServiceImpl implements CoursePublishService {
    @Autowired
    private CourseBaseInfoService courseBaseInfoService;
    @Autowired
    private TeachplanService teachplanService;
    @Autowired
    private CourseBaseMapper courseBaseMapper;
    @Autowired
    private CourseMarketMapper courseMarketMapper;
    @Autowired
    private CourseTeacherService courseTeacherService;
    @Autowired
    private CoursePublishPreMapper coursePublishPreMapper;
    @Autowired
    private CoursePublishMapper coursePublishMapper;
    @Autowired
    private MqMessageService mqMessageService;
    @Autowired
    private MediaServiceClient mediaServiceClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public CoursePreviewDto getCoursePreviewInfo(Long courseId) {
        CoursePreviewDto coursePreviewDto = new CoursePreviewDto();
        // 根据课程id查询 课程基本信息、营销信息
        CourseBaseInfoDto courseBaseInfo = courseBaseInfoService.queryCourseInfo(courseId);
        // 根据课程id，查询课程计划
        List<TeachplanDto> teachplanDtos = teachplanService.selectTreeNodes(courseId);
        // 封装返回
        coursePreviewDto.setCourseBase(courseBaseInfo);
        coursePreviewDto.setTeachplans(teachplanDtos);
        return coursePreviewDto;
    }

    @Override
    public void commitAudit(Long companyId, Long courseId) {
        //约束校验
        //课程审核状态
        CourseBase courseBase = courseBaseMapper.selectById(courseId);
        CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
        List<TeachplanDto> teachplanDtos = teachplanService.selectTreeNodes(courseId);
        List<CourseTeacher> courseTeachers = courseTeacherService.list(courseId);
        CourseBaseInfoDto courseBaseInfoDto = courseBaseInfoService.queryCourseInfo(courseId);
        String status = courseBase.getAuditStatus();
        //当前审核状态为已提交不允许再次提交
        if ("202003".equals(status)){
            XueChengPlusException.cast("当前审核状态为已提交不允许再次提交");
        }
        //本机构只允许提交本机构的课程
        if (!courseBaseInfoDto.getCompanyId().equals(companyId)){
            XueChengPlusException.cast("本机构只允许提交本机构的课程");
        }
        //课程图片是否填写
        if (StringUtils.isEmpty(courseBaseInfoDto.getPic())){
            XueChengPlusException.cast("课程图片不能为空");
        }
        //判断查询课程计划
        if (teachplanDtos==null||teachplanDtos.size()==0){
            XueChengPlusException.cast("课程计划不能为空");
        }
        //添加课程预发布记录
        CoursePublishPre coursePublishPre = new CoursePublishPre();
        //课程基本信息加部分营销信息
        BeanUtils.copyProperties(courseBaseInfoDto,coursePublishPre);
        //课程营销信息
        //转为json
        String jsonMarket = JSON.toJSONString(courseMarket);
        //将课程营销信息json数据放入课程预发布表
        coursePublishPre.setMarket(jsonMarket);
        //查询课程计划信息
        //转json
        String jsonTeachplanDtos = JSON.toJSONString(teachplanDtos);
        coursePublishPre.setTeachplan(jsonTeachplanDtos);
        //设置预发布记录状态,已提交
        coursePublishPre.setStatus("202003");
        //教学机构id
        coursePublishPre.setCompanyId(companyId);
        //提交时间
        coursePublishPre.setAuditDate(LocalDateTime.now());
            //添加课程预发布记录
        CoursePublishPre pre = coursePublishPreMapper.selectById(courseId);
        if (pre==null){
            coursePublishPreMapper.insert(coursePublishPre);
        }else {
            coursePublishPreMapper.updateById(coursePublishPre);
        }
        //更新课程基本表的审核状态
        courseBase.setAuditStatus("202003");
        courseBaseMapper.updateById(courseBase);
    }
    @Transactional
    @Override
    public void publishCourse(Long companyId, Long courseId) {
        //查询欲发表
        CoursePublishPre coursePublishPre = coursePublishPreMapper.selectById(courseId);
        if (coursePublishPre==null){
            XueChengPlusException.cast("该课程没有提交审核，无法发布");
        }
        String status = coursePublishPre.getStatus();
        if (!status.equals("202004")){
            XueChengPlusException.cast("审核未通过");
        }
        // 1.3 本机构只允许发布本机构的课程
        if (!coursePublishPre.getCompanyId().equals(companyId)) {
            XueChengPlusException.cast("操作失败，本机构只允许发布本机构的课程");
        }
        //向发布表中插入数据
        CoursePublish coursePublish = new CoursePublish();
        BeanUtils.copyProperties(coursePublishPre,coursePublish);
        coursePublish.setStatus("203002");
        CoursePublish coursePublishObj = coursePublishMapper.selectById(courseId);
        if (coursePublishObj==null){
            coursePublishMapper.insert(coursePublish);
        }else {
            coursePublishMapper.updateById(coursePublish);
        }
        // 更新课程基本信息表的发布状态为已发布
        CourseBase courseBase = courseBaseMapper.selectById(courseId);
        courseBase.setStatus("203002");
        courseBaseMapper.updateById(courseBase);
        //向消息中插入数据
        saveCoursePublishMessage(courseId);
        //删除课程预发布表对应记录
        coursePublishPreMapper.deleteById(courseId);
    }

    @Override
    public File generateCourseHtml(Long courseId) {
        //静态化文件
        File htmlFile  = null;

        try {
            //配置freemarker
            Configuration configuration = new Configuration(Configuration.getVersion());
            //加载模板
            //选指定模板路径,classpath下templates下
            //得到classpath路径
            String classpath = this.getClass().getResource("/").getPath();
            configuration.setDirectoryForTemplateLoading(new File(classpath + "/templates/"));
            //设置字符编码
            configuration.setDefaultEncoding("utf-8");

            //指定模板文件名称
            Template template = configuration.getTemplate("course_template.ftl");

            //准备数据
            CoursePreviewDto coursePreviewInfo = this.getCoursePreviewInfo(courseId);

            Map<String, Object> map = new HashMap<>();
            map.put("model", coursePreviewInfo);

            //静态化
            //参数1：模板，参数2：数据模型
            String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
//            System.out.println(content);
            //将静态化内容输出到文件中
            InputStream inputStream = IOUtils.toInputStream(content);
            //创建静态化文件
            htmlFile = File.createTempFile("course",".html");
            log.debug("课程静态化，生成静态文件:{}",htmlFile.getAbsolutePath());
            //输出流
            FileOutputStream outputStream = new FileOutputStream(htmlFile);
            IOUtils.copy(inputStream, outputStream);
        } catch (Exception e) {
            log.error("课程静态化异常:{}",e.toString());
            XueChengPlusException.cast("课程静态化异常");
        }

        return htmlFile;
    }

    @Override
    public void uploadCourseHtml(Long courseId, File file) {
        MultipartFile multipartFile = MultipartSupportConfig.getMultipartFile(file);
        String course = mediaServiceClient.uploadFile(multipartFile, "course/"+courseId+".html");
        if(course==null){
            XueChengPlusException.cast("上传静态文件异常");
        }
    }

    @Override
    public CoursePublish getCoursePunlish(Long courseId) {
        return coursePublishMapper.selectById(courseId);
    }

    @Override
    public CoursePublish getCoursePunlishCache(Long courseId) {
        //从缓存中获取数据
        Object jsonObj = redisTemplate.opsForValue().get("course:" + courseId);
        if ("null".equals(jsonObj)){
            return null;
        }
        if (jsonObj!=null){
            //如果缓存中存在数据
            CoursePublish coursePublish = JSON.parseObject(jsonObj.toString(), CoursePublish.class);
            return coursePublish;
        }
        RLock lock = redissonClient.getLock("courseLock:" + courseId);
        lock.lock();
        try {
            //设置锁防止缓存击穿
            //线程拿到锁后再次判断缓存中是否有数据
            //从缓存中获取数据
            jsonObj = redisTemplate.opsForValue().get("course:" + courseId);
            if ("null".equals(jsonObj)){
                return null;
            }
            if (jsonObj!=null){
                //如果缓存中存在数据
                CoursePublish coursePublish = JSON.parseObject(jsonObj.toString(), CoursePublish.class);
                return coursePublish;
            }
            //缓存中没有数据，从数据库中查询数据
            CoursePublish coursePublish = coursePublishMapper.selectById(courseId);
            if (coursePublish==null){
                //将数据库本不存在的课程id所对应的value值设置为null防止缓存穿透，记住设置缓存时间
                redisTemplate.opsForValue().set("course:"+courseId,"null",30+new Random().nextInt(100), TimeUnit.SECONDS);
                return null;
            }
            //向缓存中存放数据,缓存时间设置为随机时间防止缓存雪崩
            redisTemplate.opsForValue().set("course:"+courseId,JSON.toJSONString(coursePublish),30+new Random().nextInt(100), TimeUnit.SECONDS);
            return coursePublish;
        } finally {
            lock.unlock();//完成业务后释放锁
        }


    }

    /**
     * @description 保存消息表记录
     * @param courseId  课程id
     * @return void
     * @author Mr.M
     * @date 2022/9/20 16:32
     */
    private void saveCoursePublishMessage(Long courseId){
        MqMessage mqMessage = mqMessageService.addMessage("course_publish", String.valueOf(courseId), null, null);
        if(mqMessage==null){
            XueChengPlusException.cast(CommonError.UNKOWN_ERROR);
        }
    }
}
